package com.xykj.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xykj.common.entry.NewStockCalendar;
import com.xykj.mapper.NewStockCalendarMapper;
import com.xykj.service.FCrawlerService;
import com.xykj.util.JsoupUtil;
import com.xykj.web.websocket.NewsWebSocketHandler;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 新股上市日历数据爬虫类
 */
@Slf4j
@Service
public class NewStockCrawlerService implements FCrawlerService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private NewStockCalendarMapper newStockCalendarMapper;

    @Autowired
    private NewsWebSocketHandler webSocketHandler;

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 定时触发：通过@Scheduled每天凌晨2点自动执行。
     * 请求构造：使用Jsoup构造请求，设置随机User-Agent和请求头，模拟浏览器访问。
     * 数据解析：通过多种选择器定位数据表格，解析股票名称、代码和上市日期，利用正则表达式提取日期。
     * 数据存储：将解析结果暂存到列表，批量删除旧数据后插入新数据到数据库。
     * 实时推送：通过WebSocket将最新数据推送给前端。
     * 异常处理：捕获并记录异常，确保爬虫健壮性。
     */

    @Scheduled(cron = "0 0 2 * * ?")
//    @Scheduled(cron = "0 * * * * ?")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean crawl() {
        String baseUrl = "https://vip.stock.finance.sina.com.cn/corp/go.php/vRPD_NewStockIssue/page/";
        int maxPages = 10; // 最大爬取页数，防止无限循环
        boolean hasMorePages = true;
        int currentPage = 1;

        // 用于批量处理的集合
        List<NewStockCalendar> newStockList = new ArrayList<>();
        Map<Integer, Integer> pageCountMap = new HashMap<>();

        try {
            log.info("开始爬取新股上市日历数据");
            // 添加随机User-Agent和额外请求头
            Map<String, String> headers = new HashMap<>();
            headers.put("Referer", "https://finance.sina.com.cn/");
            headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");

            while (hasMorePages && currentPage <= maxPages) {
                String url = baseUrl + currentPage + ".phtml";
                log.info("开始爬取第 {} 页新股上市日历数据，URL: {}", currentPage, url);

                // 使用自定义Jsoup请求获取文档
                Connection connection = Jsoup.connect(url)
                        .userAgent(JsoupUtil.getRandomUserAgent())
                        .timeout(15000)
                        .headers(headers)
                        .ignoreContentType(true)
                        .ignoreHttpErrors(true);

                Document document = connection.get();

                // 尝试多种可能的选择器
                Elements tableRows = document.select("#NewStockTable tr");
                if (tableRows.isEmpty()) {
                    tableRows = document.select("table.table_yjl tr");
                }
                if (tableRows.isEmpty()) {
                    tableRows = document.select(".table-wrap table tr");
                }
                if (tableRows.isEmpty()) {
                    tableRows = document.select("table tr");
                }

                int pageRowCount = Math.max(0, tableRows.size() - 1); // 减去表头行
                log.info("第 {} 页解析到 {} 条新股上市日历数据", currentPage, pageRowCount);
                int pageCount = 0;

                // 跳过表头行
                for (int i = 1; i < tableRows.size(); i++) {
                    Element row = tableRows.get(i);
                    Elements cells = row.select("td");
                    if (cells.size() < 3) {
                        continue;
                    }

                    // 解析证券简称和上市日期
                    String stockName = "";
                    String stockCode = "";
                    String listingDateStr = "";

                    // 尝试找到证券代码和简称
                    for (int j = 0; j < Math.min(cells.size(), 3); j++) {
                        String cellText = cells.get(j).text().trim();

                        // 证券代码通常是6位数字
                        if (cellText.matches("\\d{6}")) {
                            stockCode = cellText;
                        }
                        // 证券简称通常是短文本
                        else if (!cellText.isEmpty() && cellText.length() < 10 && !cellText.matches("\\d+.*")) {
                            stockName = cellText;
                        }
                    }

                    // 尝试找到上市日期
                    Pattern datePattern = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
                    for (Element cell : cells) {
                        String cellText = cell.text().trim();
                        Matcher matcher = datePattern.matcher(cellText);
                        if (matcher.find()) {
                            listingDateStr = matcher.group();
                            break;
                        }
                    }
                    if (stockName.isEmpty() || listingDateStr.isEmpty()) {
                        continue;
                    }
                    LocalDateTime now = LocalDateTime.now();
                    NewStockCalendar newStockCalendar = new NewStockCalendar();
                    newStockCalendar.setStockName(stockName);
                    newStockCalendar.setListingDate(parseDate(listingDateStr));
                    newStockCalendar.setCreatedAt(now);
                    newStockCalendar.setUpdatedAt(now);
                    // 收集数据而不是立即发送
                    newStockList.add(newStockCalendar);
                    pageCount++;
                }

                pageCountMap.put(currentPage, pageCount);
                log.info("第 {} 页成功收集 {} 条新股上市日历数据", currentPage, pageCount);

                // 检查是否有下一页
                Elements nextPageLinks = document.select("a:contains(下一页)");
                if (nextPageLinks.isEmpty() || pageCount == 0) {
                    hasMorePages = false;
                } else {
                    currentPage++;
                    // 添加延迟，避免请求过于频繁
                    Thread.sleep(2000 + (long)(Math.random() * 1000));
                }
            }
            // 批量更新数据库
            if (!newStockList.isEmpty()) {
                // 获取现有记录ID
                List<Long> existingIds = newStockCalendarMapper.selectExistingIds();
                // 批量删除旧数据
                if (!existingIds.isEmpty()) {
                    newStockCalendarMapper.batchDelete(existingIds);
                }
                // 批量插入新数据
                newStockCalendarMapper.batchInsert(newStockList);
                // WebSocket推送
                webSocketHandler.broadcast(newStockList);
                log.info("成功更新{}条新股数据", newStockList.size());

            } else {
                log.info("未爬取到任何新股上市日历数据");
            }
            return true;
        } catch (Exception e) {
            log.error("爬取新股上市日历数据失败", e);
            return false;
        }
    }

    // "2025-06-02" 格式转换
    private LocalDate parseDate(String dateStr) {
        if (dateStr == null || dateStr.trim().isEmpty()) {
            throw new IllegalArgumentException("Date string cannot be null or empty");
        }

        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            return LocalDate.parse(dateStr, formatter);
        } catch (DateTimeParseException e) {
            throw new IllegalArgumentException("Invalid date format. Expected format: yyyy-MM-dd HH:mm:ss", e);
        }
    }

    private List<NewStockCalendar> parseResponse(String json) {
        List<NewStockCalendar> result = new ArrayList<>();
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(json);
            JsonNode dataNode = rootNode.path("data").path("data");

            dataNode.forEach(node -> {
                NewStockCalendar stock = new NewStockCalendar();
                stock.setStockName(node.path("securityShortName").asText());
                stock.setListingDate(LocalDate.parse(node.path("listingDate").asText(), DATE_FORMATTER));
                stock.setCreatedAt(LocalDateTime.now());
                result.add(stock);
            });
        } catch (Exception e) {
            log.error("新股日历解析失败", e);
        }
        return result;
    }
}